package com.jingdianjichi.redis.util;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * @author 邢晨旭
 * {@code @date} 2023/8/28
 */
@Component
@Slf4j
@ConditionalOnBean(RedissonClient.class)
public class LockService {

    @Resource
    RedissonClient redissonClient;

    /**
     * 分布式锁 有异常的 无参有返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param unit     时间单位
     * @param supplier 上锁的代码块
     * @param <T>      返回值类型
     * @return 代码块的返回值
     * @throws Throwable 异常
     */
    public <T> T lockWithThrowable(String key, long waitTime, TimeUnit unit, SupplierThrowable<T> supplier) throws Throwable {
        RLock lock = redissonClient.getLock(key);
        if (lock.tryLock(waitTime, unit)) {
            try {
                return supplier.get();
            } finally {
                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
        throw new RuntimeException("获取锁失败");
    }

    /**
     * 分布式锁 有异常的 无参有返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param supplier 上锁的代码块
     * @param <T>      返回值类型
     * @return 代码块的返回值
     */
    @SneakyThrows
    public <T> T lockWithThrowable(String key, long waitTime, SupplierThrowable<T> supplier) {
        return lockWithThrowable(key, waitTime, TimeUnit.SECONDS, supplier);
    }

    /**
     * 分布式锁 有异常的 无参有返回值
     *
     * @param key      锁的名字
     * @param supplier 上锁的代码块
     * @param <T>      返回值类型
     * @return 代码块的返回值
     */
    @SneakyThrows
    public <T> T lockWithThrowable(String key, SupplierThrowable<T> supplier) {
        return lockWithThrowable(key, -1, supplier);
    }

    /**
     * 分布式锁 有异常的 接收 T 对象，无返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param unit     时间单位
     * @param consumer 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     * @throws Throwable 异常
     */
    public <T> void lockWithThrowable(String key, long waitTime, TimeUnit unit, ConsumerThrowable<T> consumer, T t) throws Throwable {
        RLock lock = redissonClient.getLock(key);
        if (lock.tryLock(waitTime, unit)) {
            try {
                consumer.accept(t);
                return;
            } finally {
                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
        throw new RuntimeException("获取锁失败");
    }

    /**
     * 分布式锁 有异常的 接收 T 对象，无返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param consumer 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     * @throws Throwable 异常
     */

    public <T> void lockWithThrowable(String key, long waitTime, ConsumerThrowable<T> consumer, T t) throws Throwable {
        lockWithThrowable(key, waitTime, TimeUnit.SECONDS, consumer, t);
    }

    /**
     * 分布式锁 有异常的 接收 T 对象，无返回值
     *
     * @param key      锁的名字
     * @param consumer 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     * @throws Throwable 异常
     */
    public <T> void lockWithThrowable(String key, ConsumerThrowable<T> consumer, T t) throws Throwable {
        lockWithThrowable(key, -1, consumer, t);
    }

    /**
     * 分布式锁 有异常的 接收 T 对象并返回 boolean
     *
     * @param key       锁的名字
     * @param waitTime  等待锁的时间
     * @param unit      时间单位
     * @param predicate 上锁的代码块
     * @param t         参数
     * @param <T>       参数类型
     * @return boolean
     * @throws Throwable 异常
     */
    public <T> Boolean lockWithThrowable(String key, long waitTime, TimeUnit unit, PredicateThrowable<T> predicate, T t) throws Throwable {
        RLock lock = redissonClient.getLock(key);
        if (lock.tryLock(waitTime, unit)) {
            try {
                return predicate.test(t);
            } finally {
                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
        throw new RuntimeException("获取锁失败");
    }

    /**
     * 分布式锁 有异常的 接收 T 对象并返回 boolean
     *
     * @param key       锁的名字
     * @param waitTime  等待锁的时间
     * @param predicate 上锁的代码块
     * @param t         参数
     * @param <T>       参数类型
     * @return boolean
     * @throws Throwable 异常
     */
    public <T> Boolean lockWithThrowable(String key, long waitTime, PredicateThrowable<T> predicate, T t) throws Throwable {
        return lockWithThrowable(key, waitTime, TimeUnit.SECONDS, predicate, t);
    }

    /**
     * 分布式锁 有异常的 接收 T 对象并返回 boolean
     *
     * @param key       锁的名字
     * @param predicate 上锁的代码块
     * @param t         参数
     * @param <T>       参数类型
     * @return boolean
     * @throws Throwable 异常
     */
    public <T> Boolean lockWithThrowable(String key, PredicateThrowable<T> predicate, T t) throws Throwable {
        return lockWithThrowable(key, -1, predicate, t);
    }

    /**
     * 分布式锁 有异常的 接收 T 对象，返回 R 对象
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param unit     时间单位
     * @param function 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     * @param <R>      返回值类型
     * @return R
     * @throws Throwable 异常
     */
    public <T, R> R lockWithThrowable(String key, long waitTime, TimeUnit unit, FunctionThrowable<T, R> function, T t) throws Throwable {
        RLock lock = redissonClient.getLock(key);
        if (lock.tryLock(waitTime, unit)) {
            try {
                return function.apply(t);
            } finally {
                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
        throw new RuntimeException("获取锁失败");
    }

    /**
     * 分布式锁 有异常的 接收 T 对象，返回 R 对象
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param function 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     * @param <R>      返回值类型
     * @return R
     * @throws Throwable 异常
     */
    public <T, R> R lockWithThrowable(String key, long waitTime, FunctionThrowable<T, R> function, T t) throws Throwable {
        return lockWithThrowable(key, waitTime, TimeUnit.SECONDS, function, t);
    }

    /**
     * 分布式锁 有异常的 接收 T 对象，返回 R 对象
     *
     * @param key      锁的名字
     * @param function 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     * @param <R>      返回值类型
     * @return R
     * @throws Throwable 异常
     */
    public <T, R> R lockWithThrowable(String key, FunctionThrowable<T, R> function, T t) throws Throwable {
        return lockWithThrowable(key, -1, function, t);
    }

    /**
     * 分布式锁 有异常的 无参无返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param unit     时间单位
     * @param action   上锁的代码块
     * @param <T>      参数类型
     * @throws Throwable 异常
     */
    public <T> void lockWithThrowable(String key, long waitTime, TimeUnit unit, ActionThrowable action) throws Throwable {
        RLock lock = redissonClient.getLock(key);
        if (lock.tryLock(waitTime, unit)) {
            try {
                action.execute();
                return;
            } finally {
                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
        throw new RuntimeException("获取锁失败");
    }

    /**
     * 分布式锁 有异常的 无参无返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param action   上锁的代码块
     * @param <T>      参数类型
     * @throws Throwable 异常
     */
    public <T> void lockWithThrowable(String key, long waitTime, ActionThrowable action) throws Throwable {
        lockWithThrowable(key, waitTime, TimeUnit.SECONDS, action);
    }

    /**
     * 分布式锁 有异常的 无参无返回值
     *
     * @param key    锁的名字
     * @param action 上锁的代码块
     * @param <T>    参数类型
     * @throws Throwable 异常
     */
    public <T> void lockWithThrowable(String key, ActionThrowable action) throws Throwable {
        lockWithThrowable(key, -1, action);
    }


    /**
     * 分布式锁 无参有返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param unit     时间单位
     * @param supplier 上锁的代码块
     * @param <T>      返回值类型
     * @return 代码块的返回值
     */
    @SneakyThrows
    public <T> T lock(String key, long waitTime, TimeUnit unit, Supplier<T> supplier) {
        return lockWithThrowable(key, waitTime, unit, supplier::get);
    }

    /**
     * 分布式锁 无参有返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param supplier 上锁的代码块
     * @param <T>      返回值类型
     * @return 代码块的返回值
     */
    public <T> T lock(String key, long waitTime, Supplier<T> supplier) {
        return lock(key, waitTime, TimeUnit.SECONDS, supplier);
    }

    /**
     * 分布式锁 无参有返回值
     *
     * @param key      锁的名字
     * @param supplier 上锁的代码块
     * @param <T>      返回值类型
     * @return 代码块的返回值
     */
    public <T> T lock(String key, Supplier<T> supplier) {
        return lock(key, -1, supplier);
    }

    /**
     * 分布式锁 接收 T 对象，无返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param unit     时间单位
     * @param consumer 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     */
    @SneakyThrows
    public <T> void lock(String key, long waitTime, TimeUnit unit, Consumer<T> consumer, T t) {
        lockWithThrowable(key, waitTime, unit, consumer::accept, t);
    }

    /**
     * 分布式锁 接收 T 对象，无返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param consumer 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     */
    public <T> void lock(String key, long waitTime, Consumer<T> consumer, T t) {
        lock(key, waitTime, TimeUnit.SECONDS, consumer, t);
    }

    /**
     * 分布式锁 接收 T 对象，无返回值
     *
     * @param key      锁的名字
     * @param consumer 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     */
    public <T> void lock(String key, Consumer<T> consumer, T t) {
        lock(key, -1, consumer, t);
    }

    /**
     * 分布式锁 接收 T 对象并返回 boolean
     *
     * @param key       锁的名字
     * @param waitTime  等待锁的时间
     * @param unit      时间单位
     * @param predicate 上锁的代码块
     * @param t         参数
     * @param <T>       参数类型
     * @return boolean
     */
    @SneakyThrows
    public <T> Boolean lock(String key, long waitTime, TimeUnit unit, Predicate<T> predicate, T t) {
        return lockWithThrowable(key, waitTime, unit, predicate::test, t);
    }

    /**
     * 分布式锁 接收 T 对象并返回 boolean
     *
     * @param key       锁的名字
     * @param waitTime  等待锁的时间
     * @param predicate 上锁的代码块
     * @param t         参数
     * @param <T>       参数类型
     * @return boolean
     */
    public <T> Boolean lock(String key, long waitTime, Predicate<T> predicate, T t) {
        return lock(key, waitTime, TimeUnit.SECONDS, predicate, t);
    }

    /**
     * 分布式锁 接收 T 对象并返回 boolean
     *
     * @param key       锁的名字
     * @param predicate 上锁的代码块
     * @param t         参数
     * @param <T>       参数类型
     * @return boolean
     */
    public <T> Boolean lock(String key, Predicate<T> predicate, T t) {
        return lock(key, -1, predicate, t);
    }

    /**
     * 分布式锁 接收 T 对象，返回 R 对象
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param unit     时间单位
     * @param function 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     * @param <R>      返回值类型
     * @return R
     */
    @SneakyThrows
    public <T, R> R lock(String key, long waitTime, TimeUnit unit, Function<T, R> function, T t) {
        return lockWithThrowable(key, waitTime, unit, function::apply, t);
    }

    /**
     * 分布式锁 接收 T 对象，返回 R 对象
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param function 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     * @param <R>      返回值类型
     * @return R
     */
    public <T, R> R lock(String key, long waitTime, Function<T, R> function, T t) {
        return lock(key, waitTime, TimeUnit.SECONDS, function, t);
    }

    /**
     * 分布式锁 接收 T 对象，返回 R 对象
     *
     * @param key      锁的名字
     * @param function 上锁的代码块
     * @param t        参数
     * @param <T>      参数类型
     * @param <R>      返回值类型
     * @return R
     */
    public <T, R> R lock(String key, Function<T, R> function, T t) {
        return lock(key, -1, function, t);
    }

    /**
     * 分布式锁 无参无返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param unit     时间单位
     * @param action   上锁的代码块
     */
    @SneakyThrows
    public void lock(String key, long waitTime, TimeUnit unit, ActionThrowable action) {
        lockWithThrowable(key, waitTime, unit, action);
    }

    /**
     * 分布式锁 无参无返回值
     *
     * @param key      锁的名字
     * @param waitTime 等待锁的时间
     * @param action   上锁的代码块
     */
    public void lock(String key, long waitTime, ActionThrowable action) {
        lock(key, waitTime, TimeUnit.SECONDS, action);
    }

    /**
     * 分布式锁 无参无返回值
     *
     * @param key    锁的名字
     * @param action 上锁的代码块
     */
    public void lock(String key, ActionThrowable action) {
        lock(key, -1, action);
    }


    @FunctionalInterface
    public interface SupplierThrowable<T> {
        /**
         * 有异常的 无参有返回值
         *
         * @return T
         * @throws Throwable 异常
         */
        T get() throws Throwable;
    }


    @FunctionalInterface
    public interface ConsumerThrowable<T> {
        /**
         * 有异常的 接收 T 对象，不返回值
         *
         * @param t 参数
         * @throws Throwable 异常
         */
        void accept(T t) throws Throwable;
    }

    @FunctionalInterface
    public interface PredicateThrowable<T> {
        /**
         * 有异常的 接收 T 对象并返回 boolean
         *
         * @param t 参数
         * @return boolean
         * @throws Throwable 异常
         */
        boolean test(T t) throws Throwable;
    }

    @FunctionalInterface
    public interface FunctionThrowable<T, R> {
        /**
         * 有异常的 接收 T 对象，返回 R 对象
         *
         * @param t 参数
         * @return R
         * @throws Throwable 异常
         */
        R apply(T t) throws Throwable;
    }

    @FunctionalInterface
    public interface ActionThrowable {
        /**
         * 有异常的 无参无返回值
         *
         * @throws Throwable 异常
         */
        void execute() throws Throwable;
    }


}


